package com.lagou.edu.factory;


import com.lagou.edu.annotation.Autowired;
import com.lagou.edu.annotation.Repository;
import com.lagou.edu.annotation.Service;

import java.io.File;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.*;

//TODO 先把所有实现放到 ClassAnnotationApplicationContext 后期再抽方法设计
public class ClassAnnotationApplicationContext {
    private final Set<Class<?>> classSet = new HashSet<>();
    private final HashMap<String, BeanDefinition> relations = new HashMap<>();
    private Boolean isRefresh = false;
    private AnnotationBeanFactory beanFactory = new AnnotationBeanFactory();
    private Class application;
    List<Class> annotationClass = new ArrayList<>() {{
        add(Service.class);
        add(Repository.class);
    }};

    public void  init(Class application){
        this.application = application;
    }

    private ClassAnnotationApplicationContext() {
        System.out.println("初始化");

    }

    static private ClassAnnotationApplicationContext instance = new ClassAnnotationApplicationContext();

    static public ClassAnnotationApplicationContext getInstance() {
        return instance;
    }

    public void refresh() throws ClassNotFoundException {
        synchronized (isRefresh) {
            if (!isRefresh) {
                isRefresh = true;
                prepareRefresh();
                prepareBeanFactory(this.application);
                beanFactory.registerResolvableDependency(relations);
                beanFactory.preInstantiateSingletons();
                isRefresh = false;
            }else {
                System.out.println("正在刷新，不会再次刷新");
            }

        }
    }

    public Object getBean(String name) {
        return this.beanFactory.getBean(name);
    }

    /**
     * 清除保存的值
     */
    protected void prepareRefresh() {
        classSet.clear();
        relations.clear();

    }

    public void prepareBeanFactory(Class application) throws ClassNotFoundException {
        scanClass(application);
        analyzeAllClass();

    }

    /**
     * 测试方法不用管
     *
     * @return
     * @deprecated
     */
    public Collection<BeanDefinition> printlnRelation() {
        return this.relations.values();
    }

    /**
     * 扫描所有类
     *
     * @param application
     * @throws ClassNotFoundException
     */
    private void scanClass(Class application) throws ClassNotFoundException {
        //包名
        System.out.println("开始扫描被注解的类");
        Package pack = application.getPackage();
        String basePack = pack.getName();
        String packName = String.join(("."), basePack.split("/"));
        //先把包名转换为路径,首先得到项目的classpath
        String classpath = application.getResource("/").getPath();
        //然后把我们的包名basPach转换为路径名
        basePack = basePack.replace(".", File.separator);
        //然后把classpath和basePack合并
        String searchPath = classpath + basePack;
        Set<String> classPath = new HashSet<>();
        findClassesByFile(packName, searchPath, classPath);

        for (String s : classPath) {
            doAddBeanClass(s);
        }
    }

    /**
     * 分析bean class的关系
     */
    public void analyzeAllClass() {
        this.classSet.forEach(item -> {
            this.relations.put(item.getSimpleName(), new BeanDefinition(item.getName(), item));
        });
        relations.values().forEach(this::analyzeClass);
    }

    private void analyzeClass(BeanDefinition beanDefinition) {
        Field[] fields = beanDefinition.getCl().getDeclaredFields();
        for (Field field : fields) {
            Annotation annotation = field.getAnnotation(Autowired.class);
            if (annotation != null) {
                if(((Autowired) annotation).value()!=null){
                    beanDefinition.relations.add(this.relations.get(((Autowired) annotation).value()));
                }else {
                    beanDefinition.relations.add(this.relations.get(field.getType().getSimpleName()));
                }
            }
        }

    }

    /**
     * 判断这个类是否包含定义的注解。如果包含就放入
     *
     * @param classPath
     */
    private void doAddBeanClass(String classPath) {
        Class cl;
        System.out.println(classPath);
        try {
            cl = Class.forName(classPath);
            if (cl == null) {
                return;
            }
            boolean haveAnnotation = false;
            for (Class annotationClass : annotationClass) {
                Annotation annotationCl = cl.getAnnotation(annotationClass);
                haveAnnotation = annotationCl != null || haveAnnotation;
            }
            if (haveAnnotation) {
                this.classSet.add(cl);
            }


        } catch (ClassNotFoundException | NoClassDefFoundError e) {
            System.out.println(e);
        }
    }

    private void findClassesByFile(String pkgName, String pkgPath, Set<String> classes) throws ClassNotFoundException {
        File dir = new File(pkgPath);
        if (!dir.exists() || !dir.isDirectory()) {
            return;
        }


        // 过滤获取目录，or class文件
        File[] dirfiles = dir.listFiles(pathname -> pathname.isDirectory() || pathname.getName().endsWith("class"));


        if (dirfiles == null || dirfiles.length == 0) {
            return;
        }


        String className;
        for (File f : dirfiles) {
            if (f.isDirectory()) {
                findClassesByFile(pkgName + "." + f.getName(),
                        pkgPath + "/" + f.getName(),
                        classes);
                continue;
            }


            // 获取类名，干掉 ".class" 后缀
            className = f.getName();
            className = className.substring(0, className.length() - 6);

            classes.add(pkgName + "." + className);
        }
    }
}
